package com.gitee.wsl.jvm.ext

import java.io.PrintWriter
import java.io.StringWriter
import java.lang.reflect.InvocationTargetException
import kotlin.reflect.KClass


fun Throwable.hasCause(exceptionClazz: KClass<out Throwable>): Boolean {
    if (exceptionClazz.isInstance(this)) return true
    return exceptionClazz.isInstance(this.getRootCause())
}


fun Throwable.getRootCause(): Throwable {
    var error = this
    while (error.cause != null) {
        error = error.cause!!
    }
    if (error is InvocationTargetException) {
        error = error.targetException
    }
    return error
}



fun Throwable.getStackTraceString(): String {
    val sw = StringWriter(256)
    val pw = PrintWriter(sw, false)
    printStackTrace(pw)
    pw.flush()
    return sw.toString()
}

fun Throwable.stacktrace() =
    StringWriter().apply { this@stacktrace.printStackTrace(PrintWriter(this)) }.toString()

fun <R> catch(defaultValue: (String) -> R, block: () -> R) =
    runCatching { block.invoke() }.getOrElse { defaultValue.invoke(it.stacktrace()) }

/**
 * 获取以指定包名为前缀的最后n行简略栈追踪（方法名+包名）
 *
 * @param stackTraceElements 栈追踪元素
 * @param packagePrefix      包前缀
 * @param lineCount          行数
 * @return 栈追踪
 */
fun stackTraceToStringBriefly(
    stackTraceElements: Array<StackTraceElement>,
    packagePrefix: String? = "",
    lineCount: Int? = null,
    prefix:String? = null
): String {
    val s = StringBuilder()
    for (i in stackTraceElements.indices) {
        val stackTraceElement = stackTraceElements[i]

        // 如果是指定的包，则收集。（未指定也收集）
        if (packagePrefix.isNullOrEmpty() || stackTraceElement.className.startsWith(packagePrefix)
        ) {
            val info = stackTraceElement.methodName + ":" + stackTraceElement.lineNumber
            s.append(prefix).append(info)
        }

        // 如果指定了行数，则收集满了就退出。（不指定则全部收集）
        if (lineCount != null && i >= lineCount) {
            break
        }
    }
    return s.toString()
}